#include "flashUser.h"
#include "factory_data.h"

flashdataTypeDef flashdata ={0};
#if defined (MODULE_INCLUDE_SUBG)
const SubgBandMap_st g_subg_map[SUBG_BAND_TYPE_NUM] = 
{
    {SUBG_BAND_868,"s868"},
    {SUBG_BAND_915,"s915"},
};
#endif

static int8_t old_factorydata_recover(void* factory_data);

static int8_t _flash_parmeter_write(uint32_t addr,uint8_t* writebuff,uint32_t len)
{
    int8_t ret = bsp_flash_erase(addr,BSP_FLASH_PAGE_SIZE);
    if(ret)
    {
        LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","param erase failed:%d,%lx\r\n",ret,addr);
        return -FLASH_ERASE_FAILED;
    }
    ret = bsp_flash_write(addr,writebuff,len);
    if(ret)
    {
        LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","param write failed:%d,%lx\r\n",ret,addr);
        return -FLASH_WRITE_FAILED;
    }

    if(memcmp(writebuff,(const uint8_t *)addr,len))
    {
        LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","param check failed:%lx\r\n",addr);
        return -FLASH_CHECK_FAILED;          
    }
    return FLASH_NORMAL;
}

static int8_t flash_parameter_write(uint32_t addr,uint8_t* writebuff,uint32_t len)
{
    int8_t ret = FLASH_NORMAL;
    switch(addr)
    {
        case OTA_INFO_PAGE_START_ADDR:
        case CONFIG_INFO_PAGE_START_ADDR:
        case MODULE_DATA_START_ADDR:
        case FACTORY_INFO_PAGE_START_ADDR:
            ret = _flash_parmeter_write(INFO_BACKUP_START_ADDR,writebuff,len);
            if(ret)
            {
                return ret;
            }
            ret = _flash_parmeter_write(addr,writebuff,len);
            if(ret)
            {
                return ret;
            }
            ret = bsp_flash_erase(INFO_BACKUP_START_ADDR,BSP_FLASH_PAGE_SIZE);
            if(ret)
            {
                LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","backup erase failed:%d,%lx\r\n",ret,addr);
                return -FLASH_ERASE_FAILED;
            }
            return ret;
        default:
        return -FLASH_INVALID_ADDR;
    }
}

static int8_t flash_parameter_read(uint32_t addr,uint8_t* read_buff,uint32_t len)
{
    switch(addr)
    {
        case OTA_INFO_PAGE_START_ADDR:
        case CONFIG_INFO_PAGE_START_ADDR:
        case MODULE_DATA_START_ADDR:
        case FACTORY_INFO_PAGE_START_ADDR:
        case INFO_BACKUP_START_ADDR:
        if(bsp_flash_read(addr,read_buff,len))
        {
            return -FLASH_READ_FAILED;
        }
        else
        {
            return FLASH_NORMAL;
        }
        default:
        return -FLASH_INVALID_ADDR;
    }
}

int8_t flash_program_write(uint32_t addr,uint8_t* writebuff,uint32_t len)
{
    if(addr < APP_TEMP_B_START_ADDR 
    || addr + len > APP_TEMP_B_START_ADDR + APP_MAX_SIZE)
    {
        return -FLASH_INVAILD_PARAM;
    }
    int8_t ret = bsp_flash_write(addr,writebuff,len);
    if(ret)
    {
        LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","program write failed:%d,%lx,%lx\r\n",ret,addr,len);
        return -FLASH_WRITE_FAILED;
    }
    return FLASH_NORMAL; 
}

int8_t flash_program_erase(uint32_t addr,uint32_t len)
{
    if(addr < APP_TEMP_B_START_ADDR 
    || addr + len > APP_TEMP_B_START_ADDR + APP_MAX_SIZE)
    {
        return -FLASH_INVAILD_PARAM;
    }
    int8_t ret = bsp_flash_sector_erase(addr,len);
    if(ret)
    {
        LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","program erase failed:%d,%lx,%lx\r\n",ret,addr,len);
        return -FLASH_ERASE_FAILED;
    }
    return FLASH_NORMAL;
}

static int8_t check_data_header(uint8_t* data,uint16_t crc_h)
{
    if(CheckCrc16(data,4,crc_h) != 1)
        return -FLASH_HEADER_CRC_FAILED;
    return FLASH_NORMAL;
}

static int8_t check_data(uint8_t* data,uint16_t len,uint16_t crc_d)
{
    if(CheckCrc16(data,len,crc_d) != 1)
        return -FLASH_DATA_CRC_FAILED;
    return FLASH_NORMAL;
}

static int8_t write_flash_data(uint32_t addr,uint16_t dataType,uint8_t* data,uint32_t datalen)
{
    if(datalen > BSP_FLASH_PAGE_SIZE - DATA_HEADER_LENGTH || NULL == data)
        return -FLASH_INVAILD_PARAM;
    
    int8_t ret = 0;
    memcpy(&flashdata.data[0],data,datalen);
    
    flashdata.header = dataType;
    if(datalen > flashdata.datalen)//version updata
        flashdata.datalen = datalen;
    if(flashdata.datalen > BSP_FLASH_PAGE_SIZE - DATA_HEADER_LENGTH)
    {
        flashdata.datalen = BSP_FLASH_PAGE_SIZE - DATA_HEADER_LENGTH;
    }

    flashdata.crc16_header = GetCrc16((uint8_t*)&flashdata,4);
    flashdata.crc16_data = GetCrc16((uint8_t*)&flashdata.data[0],flashdata.datalen);
    
    ret = flash_parameter_write(addr,(uint8_t *)&flashdata,flashdata.datalen+DATA_HEADER_LENGTH);
    if(ret < 0)
    {
        LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","write %d,%lx,%lx(%lx)\r\n",ret,addr,flashdata.datalen+DATA_HEADER_LENGTH,datalen);
        return ret;
    }

    return FLASH_NORMAL;
}

static int8_t read_flash_data(uint32_t addr,uint16_t *dataType,uint8_t* data,uint32_t datalen)
{
    if(datalen > BSP_FLASH_PAGE_SIZE - DATA_HEADER_LENGTH || NULL == data)
        return -FLASH_INVAILD_PARAM;
    
    flash_parameter_read(addr,(uint8_t*)&flashdata,BSP_FLASH_PAGE_SIZE);
    
    if(check_data_header((uint8_t*)&flashdata,flashdata.crc16_header) == FLASH_NORMAL)
    {
        if(check_data((uint8_t*)flashdata.data,flashdata.datalen,flashdata.crc16_data) == FLASH_NORMAL)
        {
            if(datalen > flashdata.datalen)
            {
                memcpy(data,flashdata.data,flashdata.datalen);
                memset(&data[flashdata.datalen],0,datalen - flashdata.datalen);
            }
            else
            {
                memcpy(data,flashdata.data,datalen);
            }
            *dataType = flashdata.header;
            return FLASH_NORMAL;
        }
        else
        {
            flashdata.header = 0;
            flashdata.datalen = 0;
            return -FLASH_DATA_CRC_FAILED;
        }
    }
    else if ((flashdata.header&0x00FF) == FACTOR_DATA_FLAG)
    {
        old_factorydata_recover((void *)&flashdata);
        return FLASH_NORMAL;
    }
    else if(flashdata.header == 0xFFFF && flashdata.datalen == 0xFFFF) // without data
    {
        flashdata.header = 0;
        flashdata.datalen = 0;
        return -FLASH_EMPTY;
    }
    else
    {
        flashdata.header = 0;
        flashdata.datalen = 0;
        return -FLASH_HEADER_CRC_FAILED;
    }
}

int8_t backup_info_recover(void)
{
    int8_t ret = flash_parameter_read(INFO_BACKUP_START_ADDR,(uint8_t*)&flashdata,BSP_FLASH_PAGE_SIZE);
    if(ret)
    {
        return -FLASH_READ_FAILED;
    }
    
    if(check_data_header((uint8_t*)&flashdata,flashdata.crc16_header) == FLASH_NORMAL
    && check_data((uint8_t*)flashdata.data,flashdata.datalen,flashdata.crc16_data) == FLASH_NORMAL)
    {
        uint32_t addr;
        switch(flashdata.header)
        {
            case DATA_CONFIG_HEADER:
                addr = CONFIG_INFO_PAGE_START_ADDR;
            break;
            case DATA_NESSARY_HEADER:
                addr = FACTORY_INFO_PAGE_START_ADDR;
            break;
            case DATA_MODULE_HEADER:
                addr = MODULE_DATA_START_ADDR;
            break;
            default:
                LOG_TS(AM_LOG_LEVEL_FATAL, "FLASH", "unknow header:%x\n", flashdata.header);
            return -FLASH_INVAILD_PARAM;
        }
        LOG_TS(AM_LOG_LEVEL_INFO, "FLASH", "start recover:%x\n", flashdata.header);
        ret = _flash_parmeter_write(addr,(uint8_t*)&flashdata,flashdata.datalen+DATA_HEADER_LENGTH);
        if(ret)
        {
            return ret;
        }
        ret = bsp_flash_erase(INFO_BACKUP_START_ADDR,BSP_FLASH_PAGE_SIZE);
        if(ret)
        {
            LOG_TS(AM_LOG_LEVEL_FATAL,"FLASH","backup erase failed:%d,%lx\r\n",ret,addr);
            return -FLASH_ERASE_FAILED;
        }
    }
    return FLASH_NORMAL;
}

int8_t write_ota_data(firmwareUpdateStatusTypeDef* data)
{
    data->dataflag = OTA_DATA_FLAG;
    int8_t ret = _flash_parmeter_write(OTA_INFO_PAGE_START_ADDR,(uint8_t *)data,sizeof(firmwareUpdateStatusTypeDef));
    if(ret)
    {
        LOG_TS(AM_LOG_LEVEL_FATAL, "FLASH", "ota data write failed:%d\n",ret);
        return ret;
    }
    return FLASH_NORMAL;
}

int8_t read_ota_data(firmwareUpdateStatusTypeDef* data)
{
  return flash_read(OTA_INFO_PAGE_START_ADDR,(uint8_t *)data, sizeof(firmwareUpdateStatusTypeDef));
}

#if defined (MODULE_INCLUDE_SUBG)
int8_t write_cfg_data(dataConfigTypeDef* data)
{
    return write_flash_data(CONFIG_INFO_PAGE_START_ADDR,DATA_CONFIG_HEADER,(uint8_t*)data,sizeof(dataConfigTypeDef));
}

int8_t read_cfg_data(dataConfigTypeDef* data)
{
    uint16_t data_type = 0;
    int8_t ret = read_flash_data(CONFIG_INFO_PAGE_START_ADDR,&data_type,(uint8_t*)data,sizeof(dataConfigTypeDef));
    if(FLASH_NORMAL == ret)
    {
        if(OLD_DATA_CONFIG_HEADER == data_type)
        {
            LOG_TS(AM_LOG_LEVEL_INFO, "FLASH", "OLD CONFG:%s\n", (char*)&data->subg_band);
            uint8_t index = 0;
            for(index = 0;index < SUBG_BAND_TYPE_NUM;index++)
            {
                if(0 == memcmp(g_subg_map[index].name,&data->subg_band,4))
                {
                    data->subg_band = g_subg_map[index].band;
                    break;
                }
            }
        }
        data->bmsPN[PN_LEN] = 0;
        data->subgsn[SUBG_SN_LEN] = 0;
    }
    return ret;
}
#endif

int8_t write_factory_data(FactoryDataNessary_st* data)
{
    return write_flash_data(FACTORY_INFO_PAGE_START_ADDR,DATA_NESSARY_HEADER,(uint8_t*)data,sizeof(FactoryDataNessary_st));
}

int8_t read_factory_data(FactoryDataNessary_st* data)
{
    uint16_t data_type = 0;
    int8_t ret = read_flash_data(FACTORY_INFO_PAGE_START_ADDR,&data_type,(uint8_t*)data,sizeof(FactoryDataNessary_st));
    if(FLASH_NORMAL == ret)
    {
        if(OLD_DATA_NESSARY_HEADER == data_type)
        {
            LOG_TS(AM_LOG_LEVEL_INFO, "FLASH", "OLD DATA\n");
        }
        data->pnArray[PN_LEN] = 0;
        data->iapKeyArray[IAPKEY_LEN] = 0;
        data->randomNumArray[RANDOM_DATA_LEN] = 0;
    }
    return ret;
}

int8_t write_module_data(ModuleConfig_st* data)
{
    return write_flash_data(MODULE_DATA_START_ADDR,DATA_MODULE_HEADER,(uint8_t*)data,sizeof(ModuleConfig_st));
}

int8_t read_module_data(ModuleConfig_st* data)
{
    uint16_t data_type = 0;
    int8_t ret = read_flash_data(MODULE_DATA_START_ADDR,&data_type,(uint8_t*)data,sizeof(ModuleConfig_st));
    return ret;
}

typedef struct{ //__packed 
    uint8_t dataflag;
    uint8_t pnArray[32];
    uint8_t randomNumArray[32];
    uint8_t iapKeyArray[32];
    uint16_t checksum;
    uint8_t  productTestRecord[6];
}dataNessaryOldTypeDef;
static int8_t old_factorydata_recover(void* factory_data)
{
    dataNessaryOldTypeDef *p_data = (dataNessaryOldTypeDef*)factory_data;
    if(p_data->dataflag != FACTOR_DATA_FLAG)    // is old format
    {
        return 0;
    }
    
    uint16_t checksum = 0;
    for(int i = 0; i < PN_LEN; i++)
    {
        checksum += p_data->pnArray[i];
    }       
    for(int i = 0; i < IAPKEY_LEN; i++)
    {
        checksum += p_data->iapKeyArray[i];
    }
    for(int i = 0; i < RANDOM_DATA_LEN; i++)
    {
        checksum += p_data->randomNumArray[i];
    }
    
    if(checksum == p_data->checksum)    // 是旧协议
    {
        // 更新Factory数据
        memcpy(g_factory_nessary.pnArray, p_data->pnArray, PN_LEN);
        memcpy(g_factory_nessary.randomNumArray, p_data->randomNumArray, RANDOM_DATA_LEN);
        memcpy(g_factory_nessary.iapKeyArray, p_data->iapKeyArray, IAPKEY_LEN);
        write_factory_data(&g_factory_nessary);
        return FLASH_NORMAL;
    }
    return 0;
}

int8_t factory_data_init(void)
{
    int8_t ret = read_factory_data(&g_factory_nessary);
    if (FLASH_NORMAL == ret)
    {
        LOG_TS(AM_LOG_LEVEL_INFO, "READ", "PN:%s,random num: %s,key: %s\n", g_factory_nessary.pnArray,g_factory_nessary.randomNumArray,g_factory_nessary.iapKeyArray);
    }
    else
    {
        LOG_TS(AM_LOG_LEVEL_INFO, "READ", "Factory Data Error:%d!\r\n",ret);
        memset(&g_factory_nessary, 0, sizeof(g_factory_nessary));
    }

    return ret;        
}